using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace GraphMyCode.Controls
{
	public class GraphEdgeDescriptor : GraphItemDescriptor
	{
		public static readonly DependencyProperty ActualThickProperty =
			DependencyProperty.Register("ActualThick", typeof(double), typeof(GraphEdgeDescriptor), new PropertyMetadata(.0));

		private static readonly Brush EdgeTextBrush = new SolidColorBrush(Colors.Blue);

		private static readonly Brush EdgeBrush = new SolidColorBrush(new Color
																		{
																			A = 0xCC,
																			R = 0xFF,
																			G = 0xFF,
																			B = 0xFF
																		});

		private static readonly Brush EdgeBackBrush = new SolidColorBrush(new Color
																			{
																				A = 0xFF,
																				R = 0xFF,
																				G = 0xFF,
																				B = 0xFF
																			});

		public static readonly DependencyProperty FromItemProperty =
			DependencyProperty.Register("FromItem", typeof(GraphItem), typeof(GraphEdgeDescriptor),
										new PropertyMetadata(null, FromItemPropertyChanged));

		public static readonly DependencyProperty IsDirectionalProperty =
			DependencyProperty.Register("IsDirectional", typeof(bool), typeof(GraphEdgeDescriptor), null);

		public static readonly DependencyProperty ThickProperty =
			DependencyProperty.Register("Thick", typeof(double), typeof(GraphEdgeDescriptor), null);

		public static readonly DependencyProperty ToItemProperty =
			DependencyProperty.Register("ToItem", typeof(GraphItem), typeof(GraphEdgeDescriptor),
										new PropertyMetadata(null, ToItemPropertyChanged));


		private GraphNodeDescriptor fromItemDesc;
		private GraphNodeDescriptor toItemDesc;

		public double ActualThick
		{
			get
			{
				return (double)GetValue(ActualThickProperty);
			}
			set
			{
				SetValue(ActualThickProperty, value);
			}
		}

		public double Thick
		{
			get
			{
				return (double)GetValue(ThickProperty);
			}
			set
			{
				SetValue(ThickProperty, value);
			}
		}

		public bool IsDirectional
		{
			get
			{
				return (bool)GetValue(IsDirectionalProperty);
			}
			set
			{
				SetValue(IsDirectionalProperty, value);
			}
		}

		public GraphItem FromItem
		{
			get
			{
				return (GraphItem)GetValue(FromItemProperty);
			}
			set
			{
				SetValue(FromItemProperty, value);
			}
		}

		public GraphItem ToItem
		{
			get
			{
				return (GraphItem)GetValue(ToItemProperty);
			}
			set
			{
				SetValue(ToItemProperty, value);
			}
		}

		internal override double Left
		{
			//NOTE: Round - used for pixel snapping.
			get
			{
				return (int)Math.Round(Math.Min(fromItemDesc.AbsoluteCentralPoint.X, toItemDesc.AbsoluteCentralPoint.X));
			}
		}

		internal override double Top
		{
			//NOTE: Round - used for pixel snapping.
			get
			{
				return (int)Math.Round(Math.Min(fromItemDesc.AbsoluteCentralPoint.Y, toItemDesc.AbsoluteCentralPoint.Y));
			}
		}

		internal override int ZIndex
		{
			get
			{
				return 1;
			}
		}

		internal override void UpdatePosition()
		{
			var g = new Grid();
			g.Children.Add(GetVisualPath());
			g.Children.Add(
				new Border
					{
						Child = new TextBlock
									{
										Text = Thick.ToString(CultureInfo.InvariantCulture),
										Foreground = EdgeTextBrush,
										FontWeight = FontWeights.Bold
									},
						Background = EdgeBackBrush,
						CornerRadius = new CornerRadius(2),
						HorizontalAlignment = HorizontalAlignment.Center,
						VerticalAlignment = VerticalAlignment.Center,
						BorderBrush = EdgeTextBrush,
						BorderThickness = new Thickness(1)
					});

			Item.Content.Content = g;
			base.UpdatePosition();
		}

		public static void ToItemPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			if (e.NewValue != null)
			{
				var graphItem = (GraphItem)e.NewValue;
				var edgeDesc = (GraphEdgeDescriptor)obj;
				edgeDesc.toItemDesc = (GraphNodeDescriptor)graphItem.ItemDescriptor;
				edgeDesc.toItemDesc.InEdges.Add(edgeDesc.Item);
			}
		}

		public static void FromItemPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
		{
			if (e.NewValue != null)
			{
				var graphItem = (GraphItem)e.NewValue;
				var edgeDesc = (GraphEdgeDescriptor)obj;
				edgeDesc.fromItemDesc = (GraphNodeDescriptor)graphItem.ItemDescriptor;
				edgeDesc.fromItemDesc.OutEdges.Add(edgeDesc.Item);
			}
		}

		private void UpdateShape()
		{
			Shape =
				GeometryFactory.CreateArrow(fromItemDesc.AbsoluteCentralPoint,
											toItemDesc.AbsoluteCentralPoint,
											fromItemDesc.Shape,
											toItemDesc.Shape,
											IsDirectional,
											ActualThick);
		}

		public Path GetVisualPath()
		{
			UpdateShape();
			var path = new Path
						{
							Fill = EdgeBrush,
							Stroke = EdgeBrush,
							Data = Shape,
							StrokeThickness = ActualThick,
						};
			return path;
		}
	}
}